#include "openmove_Prefix.h"
#include "NetBase.h"
#include "NetServer.h"
#include "hawkthreads.h"
#include "OgreEngine.h"
#include "SceneBase.h"

#include "osc/OscReceivedElements.h"
#include "osc/OscPrintReceivedElements.h"
#include "osc/OscPacketListener.h"

#include "osc/UdpSocket.h"

#include <CEGUI/elements/CEGUISlider.h>
#include <CEGUI/elements/CEGUIStaticText.h>
#include <CEGUI/elements/CEGUIScrollbar.h>
#include <CEGUI/elements/CEGUIComboBox.h>


#define NB_FRAME_BEFORE_READY 5
static unsigned int s_frameElapsed = 0;
CNetServer * CNetServer::ms_pSingleton = NULL;

CNetServer::CNetServer(COgreEngine *pEngine) : CNetBase(pEngine), m_NbClient(0), m_pOSCReceiver(NULL), m_IsAllReady(false), m_serverIsReady(false)
{
	assert(NULL == ms_pSingleton);

	setReady(false);
	m_Clients.resize(MAX_CLIENTS);

	ms_pSingleton = this;
}

CNetServer::~CNetServer(void)
{
}

void CNetServer :: Destroy(void)
{
	CNetBase::Destroy();
}

void CNetServer :: setReady(bool val, NLsocket index)
{
	std::vector<SClient>::iterator it = std::find(m_Clients.begin(), m_Clients.end(), index);
	if (index == NL_INVALID || it == m_Clients.end())
	{
		for (it = m_Clients.begin(); it != m_Clients.end(); ++it)
		{
			it->m_IsReady = val;
		}

		m_IsAllReady = val;
		m_serverIsReady = val;
	}
	else
	{
		(*it).m_IsReady = val;

		bool isReady = true;
		for (it = m_Clients.begin(); it != m_Clients.end(); ++it)
		{
			isReady = (*it).m_IsReady && (*it).m_Socket != NL_INVALID;
			if (!isReady)
				break;
		}
		m_IsAllReady = isReady && m_serverIsReady;
	}
}

void CNetServer :: Create(void)
{
	CNetBase::Create();

	m_Socket = nlOpen(SERVER_PORT, NL_TCP_PACKETS);

	if (m_Socket == NL_INVALID)
	{
		String Error = "HawkNL error:";
		Error += reinterpret_cast<char const *>(nlGetErrorStr(nlGetError()));
		OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, Error, "CNetServer::Create");

		Error = "HawkNL error:";
		Error += reinterpret_cast<char const *>(nlGetSystemErrorStr(nlGetSystemError()));
		OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, Error, "CNetServer::Create");
		return;
	}

    if (!nlListen(m_Socket))
    {
        nlClose(m_Socket);
		OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Can't listen on socket", "CNetServer::Create");
    }
 
	m_NbClient = 0;

	Ogre::LogManager::getSingleton().logMessage("Server is listening...");
#ifdef __USE_HAWKTHREAD__
	htThreadCreate(CNetServer::MainServerLoop, this, HT_FALSE);
#endif
#ifdef __USE_WIN32THREAD__
	HANDLE mainThread = CreateThread(NULL, 0, CNetServer::MainServerLoop, this, 0, NULL);
	DWORD mask = 4;
	SetThreadAffinityMask(mainThread, mask);
#endif

	m_IsReady = true;

	m_pOSCReceiver = new UdpListeningReceiveSocket(IpEndpointName(IpEndpointName::ANY_ADDRESS, OSC_PORT), this);
#ifdef __USE_HAWKTHREAD__
	htThreadCreate(CNetServer::MainOSCLoop, this, HT_FALSE);
#endif
#ifdef __USE_WIN32THREAD__
	HANDLE oscThread = CreateThread(NULL, 0, CNetServer::MainOSCLoop, this, 0, NULL);
	mask = 8;
	SetThreadAffinityMask(oscThread, mask);
#endif

}

#ifdef __USE_HAWKTHREAD__
	void * CNetServer :: MainOSCLoop(void * pNetServer)
#endif
#ifdef __USE_WIN32THREAD__
	DWORD WINAPI CNetServer :: MainOSCLoop( LPVOID pNetServer ) 
#endif
{
	static_cast<CNetServer *>(pNetServer)->m_pOSCReceiver->RunUntilSigInt();

	return 0;
}

void CNetServer :: ProcessPacket( const char *data, int size, const IpEndpointName& remoteEndpoint )
{
	if (m_Shutdown)
	{
		return;
	}

	SOpenMoveDisplayPacket const *pPacket = reinterpret_cast<SOpenMoveDisplayPacket const *>(data);

	/*
	LogManager::getSingleton().logMessage("pkt: " + StringConverter::toString(pPacket->type) + 
		" xy: " + StringConverter::toString(pPacket->user_position_h) + "," + StringConverter::toString(pPacket->user_position_v) +
		" duree: " + StringConverter::toString(pPacket->duree) +
		" eventid: " + StringConverter::toString(pPacket->eventid) +
		" params: " + pPacket->params
		);
		*/

	if (SOpenMoveDisplayPacket::EVENT != pPacket->type && SOpenMoveDisplayPacket::SYNCHRO != pPacket->type)
	{
		return;
	}

	SShared Shared(-1);
	{
		switch (pPacket->type)
		{
			case (SOpenMoveDisplayPacket::EVENT):
			{
				Shared.m_Command = CMD_EVENT;
				Shared.m_Time = getTime() + m_pEngine->m_pCurrentRenderWindow->getLastFPS() * 1.0f;
				break;
			}
			case (SOpenMoveDisplayPacket::SYNCHRO):
			{
				Shared.m_Command = CMD_SETSCENE;
				Shared.m_OSCPacket.sequence_id = pPacket->sequence_id;
				break;
			}
			default:
			{
				Shared.m_Command = CMD_NONE;
			}
		}
		
		Shared.m_FrameNumber = 0;

		memcpy(&Shared.m_OSCPacket, pPacket, sizeof(SOpenMoveDisplayPacket));

#ifdef __USE_HAWKTHREAD__
		htMutexLock(&m_QueueReadMutex);
#endif
#ifdef __USE_WIN32THREAD__
		EnterCriticalSection(&m_QueueReadSection);
#endif
		ms_pSingleton->m_CommandQueue.push_back(Shared);
#ifdef __USE_HAWKTHREAD__		
		htMutexUnlock(&m_QueueReadMutex);
#endif
#ifdef __USE_WIN32THREAD__
		LeaveCriticalSection(&m_QueueReadSection);
#endif
	}
}


static bool lastIsMovie = false;
void CNetServer :: SendSynchroPacket(void)
{
	if (NULL == m_pEngine && NULL == m_pEngine->m_pCurrentScene)
		return;

	SShared Shared(-1);
	{
		Shared.m_Command = CMD_CAMSET;
		
		//Camera *pNetCam = m_pEngine->m_pCurrentSceneManager->getCamera(m_pEngine->m_pCurrentScene->m_SceneName + "_SimpleCamera");
		Camera *pNetCam = m_pEngine->m_pCurrentScene->m_pCurrentCamera;
		Shared.m_Camera.m_Position = pNetCam->getPosition();
		Shared.m_Camera.m_Orientation = pNetCam->getOrientation();
		Shared.m_FrameNumber = m_pEngine->m_Root.getCurrentFrameNumber();
		Shared.m_Time = getTime();
		Shared.m_IsReady = m_IsAllReady;
		Shared.m_AnimationsFrame = m_pEngine->m_pCurrentScene->GetAnimationsFrame();
		Shared.m_SceneId = m_pEngine->m_pCurrentScene->m_ScenePos;
		//if (lastIsMovie)
		{
			memcpy(Shared.m_animPacket, m_pEngine->m_pCurrentScene->m_animPacket, sizeof(Shared.m_animPacket));
		}
		//else
		{
			memcpy(Shared.m_moviesFrame, CSyncManager::getSingleton().m_moviesFrame, sizeof(Shared.m_moviesFrame));
		}
		lastIsMovie = !lastIsMovie;
		Shared.m_IsMovie = lastIsMovie;

	    for(NLint i = 0; i < m_NbClient; i++)
		{			
			SendPacket(m_pClient[i], &Shared, sizeof(SShared));
		}
	}
}

bool CNetServer :: frameEnded(const FrameEvent &evt)
{
	return (!m_Shutdown);
}

static int nbcammin = 100000;
static int nbcammax = 0;

static CEGUI::StaticText *selectionText = NULL;
static int seed = 0;
bool CNetServer :: frameStarted(const FrameEvent& evt)
{
	//CSyncManager::getSingleton().m_Timer.setTime(m_pEngine->m_pCurrentScene->m_Timer.getMilliseconds());

	if (NULL == selectionText)
	{
		CEGUI::WindowManager &wmgr = CEGUI::WindowManager::getSingleton();
		selectionText = (CEGUI::StaticText*)wmgr.createWindow("TaharezLook/StaticText", (CEGUI::utf8*)String("NetMessage").c_str());
		selectionText->setPosition(CEGUI::Point(0.8f, 0.5f));
		selectionText->setSize(CEGUI::Size(0.2f, 0.1f));
		selectionText->setText("");
	}
	m_pEngine->m_pCurrentScene->m_GuiSheet->addChildWindow(selectionText);



	if (0 == m_pEngine->m_Root.getCurrentFrameNumber() % 300 && NULL != selectionText)
	{
		seed = m_pEngine->m_Root.getCurrentFrameNumber();
	}

	srand(m_pEngine->m_Root.getCurrentFrameNumber());




#ifdef __USE_HAWKTHREAD__
	htMutexLock(&m_QueueReadMutex);
#endif
#ifdef __USE_WIN32THREAD__
	EnterCriticalSection(&m_QueueReadSection);
#endif

	unsigned int current = 0;
	size_t commandSize = m_CommandQueue.size();
	for (unsigned int i = 0; i < commandSize; ++i)
	{
		SShared &Command = m_CommandQueue.at(current);
		if (Command.m_Sent == SShared::e_Status_Sent)
		{
			if (Command.m_Command == CMD_START)
			{
				//m_CommandQueue.erase(m_CommandQueue.begin());
				//m_pEngine->m_Root.setCurrentFrameNumber(Command.m_FrameNumber);
			}
			else
			{
				//if (Command.m_FrameNumber <= m_pEngine->m_Root.getCurrentFrameNumber())
				{
					switch (Command.m_Command)
					{
						case CMD_STARTANIM:
						{
							if (NULL != m_pEngine && NULL != m_pEngine->m_pCurrentScene)
							{
								m_pEngine->m_pCurrentScene->Reset();
							}

							break;
						}
						case CMD_EVENT:
						{
							if (NULL != m_pEngine && NULL != m_pEngine->m_pCurrentScene)
							{
								m_pEngine->m_pCurrentScene->addEvent(Command.m_OSCPacket, Command.m_Time);
							}
							break;
						}
						case CMD_NEXTSCENE:
						{
							//m_pEngine->startNextScene();
							if (NULL != m_pEngine)
							{
								m_pEngine->m_Tasks.push_back(CMD_NEXTSCENE);
							}
							break;
						}
						case CMD_PREVSCENE:
						{
							//m_pEngine->startPreviousScene();
							if (NULL != m_pEngine)
							{
								m_pEngine->m_Tasks.push_back(CMD_PREVSCENE);
							}
							break;
						}
						case CMD_SETSCENE:
						{
							//m_pEngine->startScene(Command.m_OSCPacket.sequence_id);
							if (NULL != m_pEngine)
							{
								//m_pEngine->m_Tasks.push_back(STask(CMD_SETSCENE, Command.m_OSCPacket.sequence_id));
								LogManager::getSingleton().logMessage("setscene " + StringConverter::toString(Command.m_OSCPacket.sequence_id));
							}
							break;
						}
						default:
						{
							break;
						}
					}
					//m_CommandQueue.erase(m_CommandQueue.begin());
				}
			}

			m_CommandQueue.erase(m_CommandQueue.begin() + current);
		}
		else
		{
			if (Command.m_Sent == SShared::e_Status_Sent)
			{
				//Command.m_Sent = SShared::e_Status_Execute;
			}
			++current;
		}
	}

	if (!m_IsAllReady && m_NbClient > 0)
	{
		if (NULL != m_pEngine && NULL != m_pEngine->m_pCurrentScene)
		{
			//m_pEngine->m_pCurrentScene->m_Timer.reset();
			//SShared Shared(-1);
			//Shared.m_Command = CMD_STARTANIM;
			//Shared.m_FrameNumber = m_pEngine->m_Root.getCurrentFrameNumber() + FRAME_LATENCY;
			//Shared.m_Time = getTime();
			//Shared.m_IsReady = m_IsAllReady;
			//m_CommandQueue.push_back(Shared);
		}
	}


#ifdef __USE_HAWKTHREAD__
	htMutexUnlock(&m_QueueReadMutex);
#endif
#ifdef __USE_WIN32THREAD__
	LeaveCriticalSection(&m_QueueReadSection);
#endif

	SendSynchroPacket();

	if (s_frameElapsed > NB_FRAME_BEFORE_READY)
		m_serverIsReady = true;
	else
		m_serverIsReady = false;

	++s_frameElapsed;

	return (CNetBase::frameStarted(evt));
}

#ifdef __USE_HAWKTHREAD__
	void * CNetServer :: MainServerLoop(void * pNetServer)
#endif
#ifdef __USE_WIN32THREAD__
	DWORD WINAPI CNetServer :: MainServerLoop( LPVOID pNetServer ) 
#endif
{
	CNetServer *pServer = static_cast<CNetServer *>(pNetServer);
    NLsocket    sock = pServer->m_Socket;
    //NLsocket    client[MAX_CLIENTS];
    //NLint       clientnum = 0;
    NLbyte      string[NL_MAX_STRING_LENGTH];
	NLbyte      message[NL_MAX_STRING_LENGTH];
    //NLint       group;
    
    memset(pServer->m_pClient, 0, sizeof(pServer->m_pClient));

	SShared Shared(-1);

    pServer->m_Group = nlGroupCreate();
    while (1)
    {
        NLint       count;
        NLsocket    newsock = NL_INVALID;

        /* check for shutdown */
        if(pServer->m_Shutdown == true)
        {
            return NULL;
        }

		/* check for a new client */
		if (NULL != pServer->m_pEngine->m_pCurrentScene)
		{
			newsock = nlAcceptConnection(sock);
		}
        
		if(newsock != NL_INVALID && pServer->m_NbClient < MAX_CLIENTS)
        {
            NLaddress   addr;
            
            nlGetRemoteAddr(newsock, &addr);
            pServer->m_pClient[pServer->m_NbClient] = newsock;
            /* add socket to the group */
            nlGroupAddSocket(pServer->m_Group, newsock);
            sprintf(message, "SERVER:Client %d connected from %s on socket %d\n", pServer->m_NbClient, nlAddrToString(&addr, reinterpret_cast<NLchar *>(string)), newsock);
			Ogre::LogManager::getSingleton().logMessage(message);

			pServer->setReady(false);
			{
				Shared.m_Command = CMD_START;
				Shared.m_FrameNumber = pServer->m_pEngine->m_Root.getCurrentFrameNumber();
				Shared.m_Time = pServer->getTime();
				Shared.m_NetId = newsock;
				Shared.m_IsReady = pServer->m_IsAllReady;

				while(!pServer->SendPacket(pServer->m_pClient[pServer->m_NbClient], &Shared, sizeof(Shared)))
				{
				}

#ifdef __USE_HAWKTHREAD__
				htMutexLock(&pServer->m_QueueReadMutex);
#endif
#ifdef __USE_WIN32THREAD__
				EnterCriticalSection(&m_QueueReadSection);
#endif
				//pServer->m_CommandQueue.push_back(Shared);

				Shared.m_Command = CMD_STARTANIM;
				Shared.m_FrameNumber = pServer->m_pEngine->m_Root.getCurrentFrameNumber() + FRAME_LATENCY;
				Shared.m_Time = pServer->getTime();
				Shared.m_IsReady = pServer->m_IsAllReady;

				pServer->m_CommandQueue.push_back(Shared);

#ifdef __USE_HAWKTHREAD__
				htMutexUnlock(&pServer->m_QueueReadMutex);
#endif
#ifdef __USE_WIN32THREAD__
				LeaveCriticalSection(&m_QueueReadSection);
#endif

				//pServer->SendPacket(pServer->m_pClient[pServer->m_NbClient], &Shared, sizeof(Shared));
			}

			pServer->m_NbClient++;
		}
        else
        {
            NLint err = nlGetError();

            if( err == NL_SYSTEM_ERROR || err == NL_NOT_LISTEN)
            {
				Ogre::LogManager::getSingleton().logMessage("\n\nServer shutdown!!!!!!!!!!!!!\n");
            }
        }

		/* check for incoming messages */
        NLsocket    s[MAX_CLIENTS];
        count = nlPollGroup(pServer->m_Group, NL_READ_STATUS, s, MAX_CLIENTS, 0);
        if(count == NL_INVALID)
        {
            NLenum err = nlGetError();
            
            if(err == NL_SYSTEM_ERROR)
            {
                sprintf(message, "nlPollGroup system error: %s", nlGetSystemErrorStr(nlGetSystemError()));
				Ogre::LogManager::getSingleton().logMessage(message);
            }
            else
            {
                sprintf(message, "nlPollGroup HawkNL error: %s", nlGetErrorStr(err));
				Ogre::LogManager::getSingleton().logMessage(message);
            }
        }
		else
		{
			if (count > 0)
			{
				for (NLint i = 0; i < count; ++i)
				{
					NLint readlen;
					while ((readlen = nlRead(s[i], &Shared, sizeof(Shared))) > 0)
					{
						if (Shared.m_Command == CMD_SETREADY)
						{
							//LogManager::getSingleton().logMessage("ready " + StringConverter::toString(Shared.m_NetId));
							pServer->setReady(true, Shared.m_NetId);
						}
					}
				}
			}
		}

		if (NULL != pServer->m_pEngine->m_pCurrentScene && pServer->m_NbClient > 0)
		{
			//pServer->SendSynchroPacket();
		}

		if (!pServer->m_Shutdown)
		{
#ifdef __USE_HAWKTHREAD__
		htMutexLock(&pServer->m_QueueReadMutex);
#endif
#ifdef __USE_WIN32THREAD__
		EnterCriticalSection(&m_QueueReadSection);
#endif
		size_t const queueSize = pServer->m_CommandQueue.size();
		for (TCommands::iterator It = pServer->m_CommandQueue.begin(); It != pServer->m_CommandQueue.end(); ++It)
		{
			if ((*It).m_Sent == SShared::e_Status_Received && pServer->m_NbClient > 0)
			{
				LogManager::getSingleton().logMessage("command: " + StringConverter::toString((*It).m_OSCPacket.eventid));

				for(NLint i = 0; i < pServer->m_NbClient; i++)
				{
					if ((*It).m_Command == CMD_NEXTSCENE || (*It).m_Command == CMD_PREVSCENE || (*It).m_Command == CMD_SETSCENE)
					{
						pServer->setReady(false);
						s_frameElapsed = 0;
						pServer->m_pEngine->m_Tasks.push_back(STask((*It).m_Command, (*It).m_OSCPacket.sequence_id));
					}

					while (!pServer->SendPacket(pServer->m_pClient[i], &(*It), sizeof(SShared)))
					{						
					}
					(*It).m_Sent = SShared::e_Status_Sent;

					LogManager::getSingleton().logMessage("command: sent");
				}				
			}
			else
			{
				(*It).m_Sent = SShared::e_Status_Sent;


				if ((*It).m_Command == CMD_NEXTSCENE || (*It).m_Command == CMD_PREVSCENE || (*It).m_Command == CMD_SETSCENE)
				{
					pServer->setReady(false);
					s_frameElapsed = 0;
					pServer->m_pEngine->m_Tasks.push_back(STask((*It).m_Command, (*It).m_OSCPacket.sequence_id));
				}

			}
		}

		if (NULL != selectionText)
		{
			selectionText->setText("allready: " + StringConverter::toString(pServer->m_IsAllReady) + "\nnot sent: " + StringConverter::toString(pServer->m_CommandQueue.size())
				+ "/" + StringConverter::toString(queueSize)
				+ "\nseed: " + StringConverter::toString(seed)
				);
		}

#ifdef __USE_HAWKTHREAD__
		htMutexUnlock(&pServer->m_QueueReadMutex);
#endif
#ifdef __USE_WIN32THREAD__
		LeaveCriticalSection(&m_QueueReadSection);
#endif
		}





#ifdef __USE_HAWKTHREAD__
		htThreadSleep(4);
		//htThreadYield();
#endif
#ifdef __USE_WIN32THREAD__
		Sleep(8);
#endif
    }
    return 0;
}

void CNetServer :: addCommand(SShared const &Shared)
{
#ifdef __USE_HAWKTHREAD__
	htMutexLock(&m_QueueReadMutex);
#endif
#ifdef __USE_WIN32THREAD__
	EnterCriticalSection(&m_QueueReadSection);
#endif
	ms_pSingleton->m_CommandQueue.push_back(Shared);
#ifdef __USE_HAWKTHREAD__	
	htMutexUnlock(&m_QueueReadMutex);
#endif
#ifdef __USE_WIN32THREAD__
	LeaveCriticalSection(&m_QueueReadSection);
#endif
}

bool CNetServer :: SendPacket(NLsocket Peer, void *pBuffer, unsigned int Size)
{
	int WriteLen = nlWrite(Peer, pBuffer, Size);

	if (WriteLen == NL_INVALID)
	{
		std::vector<SClient>::iterator it = std::find(m_Clients.begin(), m_Clients.end(), Peer);
		if (m_Clients.end() != it)
		{
			it->reset();
		}
		nlGroupDeleteSocket(m_Group, Peer);
		nlClose(Peer);
		NLenum err = nlGetError();
		--m_NbClient;
		String Error = "HawkNL error:";
		Error += reinterpret_cast<char const *>(nlGetErrorStr(err));
		Ogre::LogManager::getSingleton().logMessage("HawkNL WriteLen == NL_INVALID: " + Error);
		return (false);
	}
	if (WriteLen != Size && WriteLen != NL_INVALID)
	{
		NLenum err = nlGetError();
		String Error = "HawkNL error:";
		Error += reinterpret_cast<char const *>(nlGetErrorStr(err));
		Ogre::LogManager::getSingleton().logMessage("HawkNL WriteLen != Size: " + Error);
		return (false);
	}

	return (true);
}

Real CNetServer :: getTime(void)
{
	if (NULL == m_pEngine || NULL == m_pEngine->m_pCurrentScene)
		return 0.0f;

	return m_pEngine->m_pCurrentScene->m_Timer.getMilliseconds() + (3.0f / m_pEngine->m_pCurrentRenderWindow->getLastFPS());
}

